#pragma once

#include <asio.hpp>
#include <string>
#include "connection.h"
#include "connection_manager.h"
#include "request_handler.h"

#include "spdlog.h"

namespace flask {


/// The top-level class of the HTTP server.
class server {
 public:
  server(const server&) = delete;
  server& operator=(const server&) = delete;

  /// Construct the server to listen on the specified TCP address and port, and
  /// serve up files from the given directory.
  explicit server(const std::string& address, const std::string& port,
                  const std::string& doc_root)
      : io_context_(1),
        signals_(io_context_),
        acceptor_(io_context_),
        connection_manager_(),
        request_handler_(doc_root) {
    signals_.add(SIGINT);
    signals_.add(SIGTERM);
    signals_.add(SIGQUIT);

    auto console = spdlog::stdout_color_mt("cflask");
    do_await_stop();

    asio::ip::tcp::resolver resolver(io_context_);
    asio::ip::tcp::endpoint endpoint = *resolver.resolve(address, port).begin();
    acceptor_.open(endpoint.protocol());
    acceptor_.set_option(asio::ip::tcp::acceptor::reuse_address(true));
    acceptor_.bind(endpoint);
    acceptor_.listen();

    do_accept();
  }

  void run() { io_context_.run(); }

 private:
  void do_accept() {
    acceptor_.async_accept(
        [this](std::error_code ec, asio::ip::tcp::socket socket) {
          if (!acceptor_.is_open()) {
            return;
          }

          if (!ec) {
            connection_manager_.start(std::make_shared<connection>(
                std::move(socket), connection_manager_, request_handler_));
          }

          do_accept();
        });
  }

  void do_await_stop() {
    signals_.async_wait([this](std::error_code /*ec*/, int /*signo*/) {
      acceptor_.close();
      connection_manager_.stop_all();
    });
  }

  /// The io_context used to perform asynchronous operations.
  asio::io_context io_context_;

  /// The signal_set is used to register for process termination notifications.
  asio::signal_set signals_;

  /// Acceptor used to listen for incoming connections.
  asio::ip::tcp::acceptor acceptor_;

  /// The connection manager which owns all live connections.
  connection_manager connection_manager_;

  /// The handler for all incoming requests.
  request_handler request_handler_;
};

}  // namespace flask
